package ymy;

import ymy.common.*;

import java.util.*;

/**
 * 对于整体环境，我们考虑一个普通街区的仿真环境，如下图所示：
 * ----------------------------------------------
 * |  C         5        F        10         I  |
 * |    |--------------|    |--------------|    |
 * |    |              |    |              |    |
 * |    |              |    |              |    |
 * |  2 |              | 7  |              | 12 |
 * |    |   BUILDING   |    |    BUILDING  |    |
 * |    |--------------|    |--------------|    |
 * |  B       4          E        9          H  |
 * |    |--------------|    |--------------|    |
 * |    |              |    |              |    |
 * |    |              |    |              |    |
 * |  1 |              | 6  |              | 11 |
 * |    |   BUILDING   |    |    BUILDING  |    |
 * |    |--------------|    |--------------|    |
 * |  A         3        D         8         G  |
 * |--------------------------------------------|
 * 如上所示，整个环境包括四栋建筑，十二条街道，九个十字路口。
 */
public class Environment {
    private static Random random = new Random(System.currentTimeMillis());

    // 建筑物列表
    private List<Building> buildingList;
    private volatile int currBID = 0;

    // 道路列表
    private List<Road> roadList;
    private volatile int currRID = 0;

    // 区域列表
    private List<Area> areaList;
    private volatile int currAID = 0;

    // 路由点列表
    private List<RoutePoint> routePointList;
    private volatile int currRPID = 0;

    // 停车位列表
    private List<ParkLocation> parkLocationList;
    private volatile int currPLID = 0;

    // 发车点列表
    private List<MoveLocation> moveLocationList;
    private volatile int currMLID = 0;

    // 停车位占用情况
    private Set<ParkLocation> parkOccupationSet;
    // 活动车占用情况
    private Set<MoveLocation> moveOccupationSet;

    public Environment() {

        this.buildingList = new ArrayList<>();
        this.roadList = new ArrayList<>();
        this.areaList = new ArrayList<>();
        this.routePointList = new ArrayList<>();

        this.parkLocationList = new ArrayList<>();
        this.moveLocationList = new ArrayList<>();

        this.parkOccupationSet = new HashSet<>();
        this.moveOccupationSet = new HashSet<>();
    }


    private static List<Integer> genRandNums(int cnt, int start, int end) {
        int size = end - start + 1;

        List<Integer> list = new ArrayList<>();

        int val = 0;
        int i = 0;
        while (i < cnt) {
            val = start + random.nextInt(size);
            if (list.contains(val))
                continue;
            list.add(val);
            i++;
        }

        return list;
    }

    public static List<Integer> getParkVehicleIds(int count) {
        int[][] posarr = {
            {173, 520},
            {869, 1216},
            {1625, 2092},
            {2561, 3028}
        };

        List<Integer> list = new ArrayList<>();
        List<List<Integer>> loclist = new ArrayList<>();

        int cnt = count / 4;
        for (int i = 0; i < 4; i++) {
            List<Integer> tmplist = genRandNums(cnt, posarr[i][0], posarr[i][1]);
            list.addAll(tmplist);
            loclist.add(tmplist);
        }

        for (int i = 0; i < cnt; i++) {
            for (int j = 0; j < 4; j++) {
                System.out.printf("%d, ", loclist.get(j).get(i));
            }
            System.out.println();
        }
        System.out.println();

        return list;
    }

    private static List<Integer> genOeviNums(int cnt, int start, int end, int oev) {
        int size = end - start + 1;

        List<Integer> list = new ArrayList<>();

        int val = 0;
        int i = 0;
        while (i < cnt) {
            val = start + random.nextInt(size);
            if (list.contains(val) || val % 2 != oev)
                continue;
            list.add(val);
            i++;
        }

        return list;
    }

    public static List<Integer> getMoveVehicleIds(int count) {
        int[][] posarr = {
            {178, 355},
            {712, 889},
            {1306, 1543},
            {2020, 2257}
        };

        int[] evidity = {0, 1, 0, 1};

        List<Integer> list = new ArrayList<>();
        List<List<Integer>> loclist = new ArrayList<>();

        int cnt = count / 4;
        for (int i = 0; i < 4; i++) {
            List<Integer> tmplist = genOeviNums(cnt, posarr[i][0], posarr[i][1], evidity[i]);
            list.addAll(tmplist);
            loclist.add(tmplist);
        }

        for (int i = 0; i < cnt; i++) {
            for (int j = 0; j < 4; j++) {
                System.out.printf("%d, ", loclist.get(j).get(i));
            }
            System.out.println();
        }
        System.out.println();

        return list;
    }

    public List<Building> getBuildingList() {
        return buildingList;
    }

    public List<Road> getRoadList() {
        return roadList;
    }

    public List<Area> getAreaList() {
        return areaList;
    }

    public List<RoutePoint> getRoutePointList() {
        return routePointList;
    }

    public List<ParkLocation> getParkLocationList() {
        return parkLocationList;
    }

    public List<MoveLocation> getMoveLocationList() {
        return moveLocationList;
    }

    // 对于处于某坐标行驶在某方向的车辆，得到前进特定距离后的下一个目标路由点（不考虑转弯）
    public RoutePoint moveByRoutePoint(Coordinate c, double distance, int direction) {
        // 南北向
        double x1 = GeneralConfig.ROAD_WIDTH / 2;
        double x2 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_HLEN;
        double x3 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_HLEN * 2;

        // 东西向
        double y1 = GeneralConfig.ROAD_WIDTH / 2;
        double y2 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_VLEN;
        double y3 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_VLEN * 2;

        // 车辆当前坐标
        double x = c.getX();
        double y = c.getY();

        // 左西方向 <<<<<<<
        if (direction == GeneralConfig.WEST) {
            if (x >= x1 && x < x2) {
                if (x - x1 >= distance) {
                    return null;
                }

                if (y == y1) {
                    return routePointList.get(0);
                }
                if (y == y2) {
                    return routePointList.get(1);
                }
                if (y == y3) {
                    return routePointList.get(2);
                }
            }

            if (x >= x2 && x <= x3) {
                if (x - x2 >= distance) {
                    return null;
                }

                if (y == y1) {
                    return routePointList.get(3);
                }
                if (y == y2) {
                    return routePointList.get(4);
                }
                if (y == y3) {
                    return routePointList.get(5);
                }
            }
        }

        // 右东方向 >>>>>>>
        if (direction == GeneralConfig.EAST) {
            if (x >= x1 && x < x2) {
                if (x2 - x >= distance) {
                    return null;
                }

                if (y == y1) {
                    return routePointList.get(3);
                }
                if (y == y2) {
                    return routePointList.get(4);
                }
                if (y == y3) {
                    return routePointList.get(5);
                }
            }

            if (x >= x2 && x <= x3) {
                if (x3 - x >= distance) {
                    return null;
                }

                if (y == y1) {
                    return routePointList.get(6);
                }
                if (y == y2) {
                    return routePointList.get(7);
                }
                if (y == y3) {
                    return routePointList.get(8);
                }
            }
        }

        // 下南方向 vvvvvvv
        if (direction == GeneralConfig.SOUTH) {
            if (y >= y1 && y < y2) {
                if (y - y1 >= distance) {
                    return null;
                }

                if (x == x1) {
                    return routePointList.get(0);
                }
                if (x == x2) {
                    return routePointList.get(3);
                }
                if (x == x3) {
                    return routePointList.get(6);
                }
            }

            if (y >= y2 && y <= y3) {
                if (y - y2 >= distance) {
                    return null;
                }

                if (x == x1) {
                    return routePointList.get(1);
                }
                if (x == x2) {
                    return routePointList.get(4);
                }
                if (x == x3) {
                    return routePointList.get(7);
                }
            }
        }

        // 上北方向 ^^^^^^^
        if (direction == GeneralConfig.NORTH) {
            if (y >= y1 && y < y2) {
                if (y2 - y >= distance) {
                    return null;
                }

                if (x == x1) {
                    return routePointList.get(1);
                }
                if (x == x2) {
                    return routePointList.get(4);
                }
                if (x == x3) {
                    return routePointList.get(7);
                }
            }

            if (y >= y2 && y <= y3) {
                if (y3 - y >= distance) {
                    return null;
                }

                if (x == x1) {
                    return routePointList.get(2);
                }
                if (x == x2) {
                    return routePointList.get(5);
                }
                if (x == x3) {
                    return routePointList.get(8);
                }
            }
        }

        return null;
    }

    public MoveLocation getSpecificMoveLocation(int index) {
        return this.moveLocationList.get(index);
    }

    // 随机获取移动车辆的位置
    public MoveLocation getRandomMoveLocation() {
        int index = 0;
        MoveLocation ml = null;
        int size = this.moveLocationList.size();

        do {
            index = random.nextInt(size);
            ml = this.moveLocationList.get(index);
        } while (this.moveOccupationSet.contains(ml));

        this.moveOccupationSet.add(ml);
        return ml;
    }

    public ParkLocation getSpecificParkLocation(int index) {
        return this.parkLocationList.get(index);
    }

    // 随机获取停放车辆的位置
    public ParkLocation getRandomParkLocation() {
        int index = 0;
        ParkLocation pl = null;
        int size = this.parkLocationList.size();

        do {
            index = random.nextInt(size);
            pl = this.parkLocationList.get(index);
        } while (this.parkOccupationSet.contains(pl));

        this.parkOccupationSet.add(pl);
        return pl;
    }

    // 对基本的地图环境进行初始化
    public void initEnvironment() {

        // 初始化四栋建筑物的基本地理环境（左下-左上-右下-右上）
        double bx1 = GeneralConfig.ROAD_WIDTH + GeneralConfig.BUILDING_BORDER;
        double bx2 = GeneralConfig.ROAD_WIDTH * 2 + GeneralConfig.BUILDING_HLEN + GeneralConfig.BUILDING_BORDER * 3;
        double by1 = GeneralConfig.ROAD_WIDTH + GeneralConfig.BUILDING_BORDER;
        double by2 = GeneralConfig.ROAD_WIDTH * 2 + GeneralConfig.BUILDING_VLEN + GeneralConfig.BUILDING_BORDER * 3;
        Building leftDownBuilding = this.addBuilding(bx1, by1, GeneralConfig.BUILDING_HLEN, GeneralConfig.BUILDING_VLEN);
        this.buildingList.add(leftDownBuilding);
        Building leftUpBuilding = this.addBuilding(bx1, by2, GeneralConfig.BUILDING_HLEN, GeneralConfig.BUILDING_VLEN);
        this.buildingList.add(leftUpBuilding);
        Building rightDownBuilding = this.addBuilding(bx2, by1, GeneralConfig.BUILDING_HLEN, GeneralConfig.BUILDING_VLEN);
        this.buildingList.add(rightDownBuilding);
        Building rightUpBuilding = this.addBuilding(bx2, by2, GeneralConfig.BUILDING_HLEN, GeneralConfig.BUILDING_VLEN);
        this.buildingList.add(rightUpBuilding);

        // 初始化十二条道路的基本地理环境（从下到上，从左到右，不分横竖，注释标注有）
        double x1 = GeneralConfig.ROAD_WIDTH / 2;
        double x2 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_HLEN;
        double x3 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_HLEN * 2;
        double y1 = GeneralConfig.ROAD_WIDTH / 2;
        double y2 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_VLEN;
        double y3 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_VLEN * 2;
        Road road1 = this.addRoad(x1, y1, x1, y2, GeneralConfig.ROAD_VLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.SOUTH_NORTH);
        this.roadList.add(road1);
        Road road2 = this.addRoad(x1, y2, x1, y3, GeneralConfig.ROAD_VLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.SOUTH_NORTH);
        this.roadList.add(road2);
        Road road3 = this.addRoad(x1, y1, x2, y1, GeneralConfig.ROAD_HLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.EAST_WEST);
        this.roadList.add(road3);
        Road road4 = this.addRoad(x1, y2, x2, y2, GeneralConfig.ROAD_HLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.EAST_WEST);
        this.roadList.add(road4);
        Road road5 = this.addRoad(x1, y3, x2, y3, GeneralConfig.ROAD_HLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.EAST_WEST);
        this.roadList.add(road5);
        Road road6 = this.addRoad(x2, y1, x2, y2, GeneralConfig.ROAD_VLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.SOUTH_NORTH);
        this.roadList.add(road6);
        Road road7 = this.addRoad(x2, y2, x2, y3, GeneralConfig.ROAD_VLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.SOUTH_NORTH);
        this.roadList.add(road7);
        Road road8 = this.addRoad(x2, y1, x3, y1, GeneralConfig.ROAD_HLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.EAST_WEST);
        this.roadList.add(road8);
        Road road9 = this.addRoad(x2, y2, x3, y2, GeneralConfig.ROAD_HLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.EAST_WEST);
        this.roadList.add(road9);
        Road road10 = this.addRoad(x2, y3, x3, y3, GeneralConfig.ROAD_HLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.EAST_WEST);
        this.roadList.add(road10);
        Road road11 = this.addRoad(x3, y1, x3, y2, GeneralConfig.ROAD_VLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.SOUTH_NORTH);
        this.roadList.add(road11);
        Road road12 = this.addRoad(x3, y2, x3, y3, GeneralConfig.ROAD_VLEN, GeneralConfig.ROAD_WIDTH, GeneralConfig.SOUTH_NORTH);
        this.roadList.add(road12);

        // 初始化九个十字路口（标记如注释，记为A-I）
        RoutePoint routePoint1 = new RoutePoint(this.currRPID++);
        routePoint1.setPosition(new Coordinate(x1, y1));
        routePoint1.addDirectionRoad(GeneralConfig.NORTH, road1);
        routePoint1.addDirectionRoad(GeneralConfig.EAST, road3);
        this.routePointList.add(routePoint1);
        RoutePoint routePoint2 = new RoutePoint(this.currRPID++);
        routePoint2.setPosition(new Coordinate(x1, y2));
        routePoint2.addDirectionRoad(GeneralConfig.SOUTH, road1);
        routePoint2.addDirectionRoad(GeneralConfig.NORTH, road2);
        routePoint2.addDirectionRoad(GeneralConfig.EAST, road4);
        this.routePointList.add(routePoint2);
        RoutePoint routePoint3 = new RoutePoint(this.currRPID++);
        routePoint3.setPosition(new Coordinate(x1, y3));
        routePoint3.addDirectionRoad(GeneralConfig.EAST, road5);
        routePoint3.addDirectionRoad(GeneralConfig.SOUTH, road2);
        this.routePointList.add(routePoint3);
        RoutePoint routePoint4 = new RoutePoint(this.currRPID++);
        routePoint4.setPosition(new Coordinate(x2, y1));
        routePoint4.addDirectionRoad(GeneralConfig.NORTH, road6);
        routePoint4.addDirectionRoad(GeneralConfig.EAST, road8);
        routePoint4.addDirectionRoad(GeneralConfig.WEST, road3);
        this.routePointList.add(routePoint4);
        RoutePoint routePoint5 = new RoutePoint(this.currRPID++);
        routePoint5.setPosition(new Coordinate(x2, y2));
        routePoint5.addDirectionRoad(GeneralConfig.NORTH, road7);
        routePoint5.addDirectionRoad(GeneralConfig.EAST, road9);
        routePoint5.addDirectionRoad(GeneralConfig.WEST, road4);
        routePoint5.addDirectionRoad(GeneralConfig.SOUTH, road6);
        this.routePointList.add(routePoint5);
        RoutePoint routePoint6 = new RoutePoint(this.currRPID++);
        routePoint6.setPosition(new Coordinate(x2, y3));
        routePoint6.addDirectionRoad(GeneralConfig.WEST, road5);
        routePoint6.addDirectionRoad(GeneralConfig.SOUTH, road7);
        routePoint6.addDirectionRoad(GeneralConfig.EAST, road10);
        this.routePointList.add(routePoint6);
        RoutePoint routePoint7 = new RoutePoint(this.currRPID++);
        routePoint7.setPosition(new Coordinate(x3, y1));
        routePoint7.addDirectionRoad(GeneralConfig.WEST, road8);
        routePoint7.addDirectionRoad(GeneralConfig.NORTH, road11);
        this.routePointList.add(routePoint7);
        RoutePoint routePoint8 = new RoutePoint(this.currRPID++);
        routePoint8.setPosition(new Coordinate(x3, y2));
        routePoint8.addDirectionRoad(GeneralConfig.WEST, road9);
        routePoint8.addDirectionRoad(GeneralConfig.SOUTH, road11);
        routePoint8.addDirectionRoad(GeneralConfig.NORTH, road12);
        this.routePointList.add(routePoint8);
        RoutePoint routePoint9 = new RoutePoint(this.currRPID++);
        routePoint9.setPosition(new Coordinate(x3, y3));
        routePoint9.addDirectionRoad(GeneralConfig.WEST, road10);
        routePoint9.addDirectionRoad(GeneralConfig.SOUTH, road12);
        this.routePointList.add(routePoint9);

        // 初始化N大隔离区，九个十字路口为单独的区域，主干路段划分为若干区域
        /*
        Area area1 = this.addArea(x1, y1, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area1);
        Area area2 = this.addArea(x2, y1, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area2);
        Area area3 = this.addArea(x3, y1, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area3);
        Area area4 = this.addArea(x1, y2, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area4);
        Area area5 = this.addArea(x2, y2, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area5);
        Area area6 = this.addArea(x3, y2, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area6);
        Area area7 = this.addArea(x1, y3, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area7);
        Area area8 = this.addArea(x2, y3, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area8);
        Area area9 = this.addArea(x3, y3, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_CROSS);
        this.areaList.add(area9);
        */

        for (int i = 0; i < this.roadList.size(); i++) {
            Road targetRoad = this.roadList.get(i);
            double bx = targetRoad.getBegPos().getX();
            double by = targetRoad.getBegPos().getY();
            double ex = targetRoad.getEndPos().getX();
            double ey = targetRoad.getEndPos().getY();
            // 南北向道路
            if (targetRoad.getRoadDirection() == GeneralConfig.SOUTH_NORTH) {
                //double y = by + GeneralConfig.AREA_RADIUS * 2;
                double y = by + GeneralConfig.AREA_RADIUS;
                while (y < ey) {
                    Area area = this.addArea(bx, y, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_RSIDE);
                    this.areaList.add(area);
                    y = y + GeneralConfig.AREA_RADIUS * 2;
                }
            }
            // 东西向道路
            if (targetRoad.getRoadDirection() == GeneralConfig.EAST_WEST) {
                //double x = bx + GeneralConfig.AREA_RADIUS * 2;
                double x = bx + GeneralConfig.AREA_RADIUS;
                while (x < ex) {
                    Area area = this.addArea(x, by, GeneralConfig.AREA_RADIUS, GeneralConfig.AREA_TYPE_RSIDE);
                    this.areaList.add(area);
                    x = x + GeneralConfig.AREA_RADIUS * 2;
                }
            }
        }

        // 初始化停车位信息
        double px1 = GeneralConfig.ROAD_WIDTH + GeneralConfig.BUILDING_BORDER / 2;
        double px2 = px1 + GeneralConfig.BUILDING_HLEN + GeneralConfig.BUILDING_BORDER;
        double px3 = px2 + GeneralConfig.ROAD_WIDTH + GeneralConfig.BUILDING_BORDER;
        double px4 = px3 + GeneralConfig.BUILDING_HLEN + GeneralConfig.BUILDING_BORDER;
        double py1 = GeneralConfig.ROAD_WIDTH + GeneralConfig.BUILDING_BORDER / 2;
        double py2 = py1 + GeneralConfig.BUILDING_VLEN + GeneralConfig.BUILDING_BORDER;
        double py3 = py2 + GeneralConfig.ROAD_WIDTH + GeneralConfig.BUILDING_BORDER;
        double py4 = py3 + GeneralConfig.BUILDING_VLEN + GeneralConfig.BUILDING_BORDER;
        for (double x = px1 + GeneralConfig.PARK_GAP; x < px2; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py1);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px1 + GeneralConfig.PARK_GAP; x < px2; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py2);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px1 + GeneralConfig.PARK_GAP; x < px2; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py3);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px1 + GeneralConfig.PARK_GAP; x < px2; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py4);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px3 + GeneralConfig.PARK_GAP; x < px4; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py1);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px3 + GeneralConfig.PARK_GAP; x < px4; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py2);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px3 + GeneralConfig.PARK_GAP; x < px4; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py3);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double x = px3 + GeneralConfig.PARK_GAP; x < px4; x = x + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(x, py4);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py1 + GeneralConfig.PARK_GAP; y < py2; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px1, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py1 + GeneralConfig.PARK_GAP; y < py2; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px2, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py1 + GeneralConfig.PARK_GAP; y < py2; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px3, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py1 + GeneralConfig.PARK_GAP; y < py2; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px4, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py3  + GeneralConfig.PARK_GAP; y < py4; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px1, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py3 + GeneralConfig.PARK_GAP; y < py4; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px2, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py3 + GeneralConfig.PARK_GAP; y < py4; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px3, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }
        for (double y = py3 + GeneralConfig.PARK_GAP; y < py4; y = y + GeneralConfig.PARK_GAP) {
            Coordinate c = new Coordinate(px4, y);
            Area a = this.getAreaByCoordinate(c);
            this.parkLocationList.add(new ParkLocation(c, a));
        }

        // 初始化发车点信息
        double mx1 = GeneralConfig.ROAD_WIDTH / 2;
        double mx2 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_HLEN;
        double mx3 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_HLEN * 2;
        double my1 = GeneralConfig.ROAD_WIDTH / 2;
        double my2 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_VLEN;
        double my3 = GeneralConfig.ROAD_WIDTH / 2 + GeneralConfig.ROAD_VLEN * 2;
        for (double x = mx1 + GeneralConfig.MOVE_GAP; x < mx2; x = x + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(x, my1);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.EAST));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.WEST));
        }
        for (double x = mx1 + GeneralConfig.MOVE_GAP; x < mx2; x = x + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(x, my2);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.EAST));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.WEST));
        }
        for (double x = mx1 + GeneralConfig.MOVE_GAP; x < mx2; x = x + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(x, my3);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.EAST));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.WEST));
        }
        for (double x = mx2 + GeneralConfig.MOVE_GAP; x < mx3; x = x + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(x, my1);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.EAST));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.WEST));
        }
        for (double x = mx2 + GeneralConfig.MOVE_GAP; x < mx3; x = x + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(x, my2);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.EAST));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.WEST));
        }
        for (double x = mx2 + GeneralConfig.MOVE_GAP; x < mx3; x = x + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(x, my3);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.EAST));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.WEST));
        }
        for (double y = my1 + GeneralConfig.MOVE_GAP; y < my2; y = y + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(mx1, y);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.NORTH));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.SOUTH));
        }
        for (double y = my1 + GeneralConfig.MOVE_GAP; y < my2; y = y + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(mx2, y);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.NORTH));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.SOUTH));
        }
        for (double y = my1 + GeneralConfig.MOVE_GAP; y < my2; y = y + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(mx3, y);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.NORTH));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.SOUTH));
        }
        for (double y = my2  + GeneralConfig.MOVE_GAP; y < my3; y = y + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(mx1, y);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.NORTH));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.SOUTH));
        }
        for (double y = my2 + GeneralConfig.MOVE_GAP; y < my3; y = y + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(mx2, y);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.NORTH));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.SOUTH));
        }
        for (double y = my2 + GeneralConfig.MOVE_GAP; y < my3; y = y + GeneralConfig.MOVE_GAP) {
            Coordinate c = new Coordinate(mx3, y);
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.NORTH));
            this.moveLocationList.add(new MoveLocation(c, GeneralConfig.SOUTH));
        }
    }

    public Building addBuilding(double x, double y, double hlen, double vlen) {
        Building building = new Building();

        building.setBuildingId(this.currBID);
        building.setStartPosition(new Coordinate(x, y));
        building.setHorizontalLength(hlen);
        building.setVerticalLength(vlen);

        this.currBID++;

        return building;
    }

    public Road addRoad(double bx, double by, double ex, double ey, double len, double width, boolean direct) {
        Road road = new Road();

        road.setRoadId(this.currRID);
        road.setRoadDirection(direct);
        road.setBegPos(new Coordinate(bx, by));
        road.setEndPos(new Coordinate(ex, ey));
        road.setRoadLength(len);
        road.setRoadWidth(width);

        this.currRID++;

        return road;
    }

    public Area addArea(double x, double y, double r, int type) {
        Area area = new Area();

        area.setAreaId(this.currAID);
        area.setCentralPosition(new Coordinate(x, y));
        area.setAreaRadius(r);
        area.setType(type);

        this.currAID++;

        return area;
    }

    public Area getAreaByCoordinate(Coordinate c) {
        for (Area a : this.areaList) {
            if (a.inArea(c)) {
                return a;
            }
        }

        return null;
    }

    @Override
    public String toString() {
        String str = "[INFO] SIMULATION ENVIRONMENT INFORMATION = {";
        str += "\n  Building Count = " + this.buildingList.size();
        str += "\n  Area Count = " + this.areaList.size();
        str += "\n  Road Count = " + this.roadList.size();
        str += "\n  Park Count = " + this.parkLocationList.size();
        str += "\n  Move Count = " + this.moveLocationList.size();
        if (GeneralConfig.DEBUG_INFO) {
            str += "\n  Area Information = ";
            for (Area a : this.areaList) {
                str += "\n    [AREA DETAIL] = " + a;
            }
        }
        str += "\n}";
        return str;
    }

    // 停车位信息，主要包括停车位坐标和坐落区域
    public static class ParkLocation {
        // 停车位坐标
        private Coordinate coordinate;
        // 停车位坐落区域
        private Area area;

        public ParkLocation(Coordinate c, Area a) {
            this.coordinate = c;
            this.area = a;
        }

        public Coordinate getCoordinate() {
            return coordinate;
        }

        public void setCoordinate(Coordinate coordinate) {
            this.coordinate = coordinate;
        }

        public Area getArea() {
            return area;
        }

        public void setArea(Area area) {
            this.area = area;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }
            if (o == this) {
                return true;
            }
            if (getClass() != o.getClass()) {
                return false;
            }

            ParkLocation p = (ParkLocation)o;
            return this.coordinate.equals(p.coordinate) && this.area.equals(p.area);
        }
    }

    // 发车点信息，主要包括发车点坐标和行车方向
    public static class MoveLocation {
        // 发车点坐标
        private Coordinate coordinate;
        // 行车方向
        private int direction;

        public MoveLocation(Coordinate c, int d) {
            this.coordinate = c;
            this.direction = d;
        }

        public Coordinate getCoordinate() {
            return coordinate;
        }

        public void setCoordinate(Coordinate coordinate) {
            this.coordinate = coordinate;
        }

        public int getDirection() {
            return direction;
        }

        public void setDirection(int direction) {
            this.direction = direction;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }
            if (o == this) {
                return true;
            }
            if (getClass() != o.getClass()) {
                return false;
            }

            MoveLocation m = (MoveLocation)o;
            return this.coordinate.equals(m.coordinate) && this.direction == m.direction;
        }

        @Override
        public String toString() {
            String str = new String();
            str = "[INFO] MOVE LOCATION INFO = {";
            str += "\n  Coordinate = " + this.coordinate;
            str += "\n  Direction = " + this.direction;
            str += "\n}\n";
            return str;
        }
    }
}
